﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace System.Peppers.Wmi
{
    /// <summary>
    /// The Win32_ComputerSystem WMI class represents a computer system running Windows.
    /// </summary>
    public sealed class ComputerSystem : SystemElement
    {
        #region Static
        private static class WmiProperties
        {
            public const string AutomaticResetBootOption = "AutomaticResetBootOption",
                CurrentTimeZone = "CurrentTimeZone",
                EnableDaylightSavingsTime = "EnableDaylightSavingsTime",
                DNSHostName = "DNSHostName",
                Domain = "Domain",
                DomainRole = "DomainRole",
                Model = "Model",
                NumberOfProcessors = "NumberOfProcessors",
                PartOfDomain = "PartOfDomain",
                PrimaryOwnerContact = "PrimaryOwnerContact",
                PrimaryOwnerName = "PrimaryOwnerName",
                Roles = "Roles",
                SystemStartupDelay = "SystemStartupDelay",
                SystemStartupOptions = "SystemStartupOptions",
                SystemStartupSetting = "SystemStartupSetting",
                SystemType = "SystemType",
                TotalPhysicalMemory = "TotalPhysicalMemory",
                UserName = "UserName",
                Workgroup = "Workgroup",
                Version = "Version";
        }

        private static class WmiMethods
        {
            public const string Rename = "Rename",
                JoinDomainOrWorkgroup = "JoinDomainOrWorkgroup",
                UnjoinDomainOrWorkgroup = "UnjoinDomainOrWorkgroup";
        }

        private static class WmiParameters
        {
            public const string Name = "Name",
                Password = "Password",
                UserName = "UserName",
                AccountOU = "AccountOU",
                FJoinOptions = "FJoinOptions";
        }

        /// <summary>
        /// Enumerates computer system on the system
        /// </summary>
        /// <returns>List of computer system objects</returns>
        public static IEnumerable<ComputerSystem> GetComputerSystems()
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_ComputerSystem))
            {
                yield return new ComputerSystem(item);
            }
        }

        /// <summary>
        /// Enumerates computer system objects on a remote system
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <returns>List of computer system objects</returns>
        public static IEnumerable<ComputerSystem> GetComputerSystems(WmiRemoteParameters remoteParams)
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_ComputerSystem, remoteParams))
            {
                yield return new ComputerSystem(item);
            }
        }
        #endregion

        internal ComputerSystem(ManagementObject wmiObject)
            : base(wmiObject)
        { }

        /// <summary>
        /// If True, the automatic reset boot option is enabled.
        /// </summary>
        public bool AutomaticResetBootOption
        {
            get { return Convert.ToBoolean(this[WmiProperties.AutomaticResetBootOption] ?? false); }
            set { this[WmiProperties.AutomaticResetBootOption] = value; }
        }

        /// <summary>
        /// Amount of time the unitary computer system is offset from Coordinated Universal Time (UTC).
        /// </summary>
        public short CurrentTimeZone
        {
            get { return Convert.ToInt16(this[WmiProperties.CurrentTimeZone] ?? 0); }
            set { this[WmiProperties.CurrentTimeZone] = value; }
        }

        /// <summary>
        /// Enables daylight savings time (DST) on a computer. A value of True indicates that the system time changes to an hour ahead or behind when DST starts or ends. A value of False indicates that the system time does not change to an hour ahead or behind when DST starts or ends. A value of NULL indicates that the DST status is unknown on a system.
        /// </summary>
        public bool? EnableDaylightSavingsTime
        {
            get { return WmiUtil.ToNullable<bool>(this[WmiProperties.EnableDaylightSavingsTime]); }
            set { this[WmiProperties.EnableDaylightSavingsTime] = value; }
        }

        /// <summary>
        /// If True, the computer is part of a domain. If the value is NULL, the computer is not in a domain or the status is unknown. If you unjoin the computer from a domain, the value becomes false.
        /// </summary>
        public bool? PartOfDomain
        {
            get { return WmiUtil.ToNullable<bool>(this[WmiProperties.PartOfDomain]); }
        }

        /// <summary>
        /// List that specifies the roles of a system in the information technology environment.
        /// </summary>
        public string[] Roles
        {
            get { return this[WmiProperties.Roles] as string[]; }
            set { this[WmiProperties.Roles] = value; }
        }

        /// <summary>
        /// Time to delay before starting the Windows operating system—in seconds. Beginning with Windows Vista, SystemStartupDelay is obsolete because Boot.ini is not used to configure system startup. I will look into this...
        /// 
        /// Windows Server 2003, Windows XP, and Windows 2000:  This property is available and supported. You can use SystemStartupDelay, with SystemStartupSetting and SystemStartupOptions to modify Boot.ini system startup settings.
        /// </summary>
        public ushort SystemStartupDelay
        {
            get { return Convert.ToUInt16(this[WmiProperties.SystemStartupDelay] ?? 0); }
            set { this[WmiProperties.SystemStartupDelay] = value; }
        }

        /// <summary>
        /// List of the options for starting up the coumputer system running Windows. This property must have a value. Beginning with Windows Vista, SystemStartupOptions is obsolete because Boot.ini is not used to configure system startup. I will look into this...
        /// 
        /// Windows Server 2003, Windows XP, and Windows 2000:  This property is available and supported. You can use SystemStartupOptions, with SystemStartupSetting and SystemStartupDelay to modify Boot.ini system startup settings.
        /// </summary>
        public string[] SystemStartupOptions
        {
            get { return this[WmiProperties.SystemStartupOptions] as string[]; }
            set { this[WmiProperties.SystemStartupOptions] = value; }
        }

        /// <summary>
        /// Index of the default startup profile. This value is calculated so that it usually returns 0 (zero), because at write-time the profile string is physically moved to the top of the list, which is how Windows NT determines the default value. Beginning with Windows Vista, SystemStartupSetting is obsolete because Boot.ini is not used to configure system startup. I will look into this...
        /// 
        /// Windows Server 2003, Windows XP, and Windows 2000:  This property is available and supported. You can use SystemStartupSetting, with SystemStartupDelay and SystemStartupOptions to modify Boot.ini system startup settings.
        /// </summary>
        public byte SystemStartupSetting
        {
            get { return Convert.ToByte(this[WmiProperties.SystemStartupSetting] ?? 0); }
            set { this[WmiProperties.SystemStartupSetting] = value; }
        }

        /// <summary>
        /// System running on the Windows-based computer. This property must have a value.
        /// </summary>
        public string SystemType
        {
            get { return Convert.ToString(this[WmiProperties.SystemType]); }
        }

        /// <summary>
        /// Name of a user that is logged on currently. This property must have a value. In a terminal services session, UserName returns the name of the user that is logged on to the console—not the user logged on during the terminal service session.
        /// </summary>
        public string UserName
        {
            get { return Convert.ToString(this[WmiProperties.UserName]); }
        }

        /// <summary>
        /// Name of local computer according to the domain name server (DNS).
        /// </summary>
        public string DnsHostname
        {
            get { return Convert.ToString(this[WmiProperties.DNSHostName]); }
        }

        /// <summary>
        /// Name of the domain to which a computer belongs.
        /// </summary>
        public string Domain
        {
            get { return Convert.ToString(this[WmiProperties.Domain]); }
        }

        /// <summary>
        /// Name of the workgroup for this computer. If the value of the PartOfDomain property is False, then the name of the workgroup is returned.
        /// </summary>
        public string Workgroup
        {
            get { return Convert.ToString(this[WmiProperties.Workgroup]); }
            set { this[WmiProperties.Workgroup] = value; }
        }

        /// <summary>
        /// Role of a computer in an assigned domain workgroup. A domain workgroup is a collection of computers on the same network. For example, a DomainRole property may show that a computer is a member workstation.
        /// </summary>
        public DomainRole DomainRole
        {
            get { return (DomainRole)Convert.ToUInt16(this[WmiProperties.DomainRole]); }
        }

        /// <summary>
        /// Product name that a manufacturer gives to a computer. This property must have a value.
        /// </summary>
        public string Model
        {
            get { return Convert.ToString(this[WmiProperties.Model]); }
        }

        /// <summary>
        /// Number of physical processors currently available on a system. This is the number of enabled processors for a system, which does not include the disabled processors. If a computer system has two physical processors each containing two logical processors, then the value of NumberOfProcessors is 2 and NumberOfLogicalProcessors is 4. The processors may be multicore or they may be hyperthreading processors. 
        /// 
        /// Windows Server 2003, Windows XP, and Windows 2000:  Because the NumberOfLogicalProcessors property is not available, NumberOfProcessors indicates the number of logical processors available in the system. In the case of a computer system that has two physical processors each containing two logical processors, the value of NumberOfProcessors is 4.
        /// </summary>
        public uint NumberOfProcessors
        {
            get { return Convert.ToUInt32(this[WmiProperties.NumberOfProcessors]); }
        }

        /// <summary>
        /// Contact information for the primary system owner, for example, phone number, e-mail address, and so on.
        /// </summary>
        public string PrimaryOwnerContact
        {
            get { return Convert.ToString(this[WmiProperties.PrimaryOwnerContact]); }
        }

        /// <summary>
        /// Name of the primary system owner. 
        /// </summary>
        public string PrimaryOwnerName
        {
            get { return Convert.ToString(this[WmiProperties.PrimaryOwnerName]); }
        }

        public ulong TotalPhysicalMemory
        {
            get { return Convert.ToUInt64(this[WmiProperties.TotalPhysicalMemory]); }
        }

        /// <summary>
        /// You can use the Rename method to rename a computer—if you are a member of the local administrator group. However, you cannot use the method remotely for domain computers.
        /// </summary>
        /// <param name="name">New computer name. The value of this parameter cannot include control characters, leading or trailing spaces, or any of the following characters: / \\ [ ].</param>
        /// <param name="password">Password to use when connecting to the domain controller if the UserName parameter specifies an account name. Otherwise, this parameter must be NULL. See the Remarks section of this topic for more information about Password and UserName parameters.</param>
        /// <param name="username">String that specifies the account name to use when connecting to the domain controller. The string must be null-terminated, and must specify a domain NetBIOS name and user account—for example, "DOMAINNAME\username" or "someone@domainname.com", which is a user principal name (UPN). If the UserName parameter is NULL, WMI uses the context of the caller. See the Remarks section of this topic for more information about Password and UserName parameters.</param>
        /// <returns>Result of method</returns>
        public ManagementStatus Rename(string name, string password, string username)
        {
            return ExecuteStatusMethod(WmiMethods.Rename, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.Name] = name;
                inParam[WmiParameters.Password] = password;
                inParam[WmiParameters.UserName] = username;
            });
        }

        /// <summary>
        /// The JoinDomainOrWorkgroup method joins a computer system to a domain or workgroup.
        /// </summary>
        /// <param name="name">Specifies the domain or workgroup to join. Cannot be NULL.</param>
        /// <param name="password">If the UserName parameter specifies an account name, the Password parameter must point to the password to use when connecting to the domain controller. Otherwise, this parameter must be NULL.</param>
        /// <param name="username">Pointer to a constant null-terminated character string that specifies the account name to use when connecting to the domain controller. Must specify a domain NetBIOS name and user account, for example, Domain\user. If this parameter is NULL, the caller information is used.</param>
        /// <param name="accountOU">Specifies the pointer to a constant null-terminated character string that contains the RFC 1779 format name of the organizational unit (OU) for the computer account. If you specify this parameter, the string must contain a full path, otherwise Accent must be NULL.</param>
        /// <param name="joinOptions">Set of bit flags that define the join options.</param>
        /// <returns>Result of method</returns>
        public ManagementStatus JoinDomainOrWorkgroup(string name, string password, string username, string accountOU, JoinDomainOptions joinOptions)
        {
            return ExecuteStatusMethod(WmiMethods.JoinDomainOrWorkgroup, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.Name] = name;
                inParam[WmiParameters.Password] = password;
                inParam[WmiParameters.UserName] = username;
                inParam[WmiParameters.AccountOU] = accountOU;
                inParam[WmiParameters.FJoinOptions] = joinOptions;
            });
        }

        /// <summary>
        /// The UnjoinDomainOrWorkgroup method removes a computer system from a domain or workgroup.
        /// </summary>
        /// <param name="password">If the UserName parameter specifies an account name, the Password parameter must point to the password to use when connecting to the domain controller. Otherwise, this parameter must be NULL.</param>
        /// <param name="username">Pointer to a constant null-terminated character string that specifies the account name to use when connecting to the domain controller. Must specify a domain NetBIOS name and user account, for example, Domain\user. If this parameter is NULL, the caller information is used.</param>
        /// <param name="joinOptions">Set of bit flags that define the join options.</param>
        /// <returns>Result of method</returns>
        public ManagementStatus UnjoinDomainOrWorkgroup(string password, string username, JoinDomainOptions joinOptions)
        {
            return ExecuteStatusMethod(WmiMethods.UnjoinDomainOrWorkgroup, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.Password] = password;
                inParam[WmiParameters.UserName] = username;
                inParam[WmiParameters.FJoinOptions] = joinOptions;
            });
        }
    }
}